<script setup lang="ts">
import type { Slots } from 'vue';
import { computed, ref, useSlots, watch } from 'vue';
import { useWatcher } from '@sa/alova/client';
import { alova } from '@/service/request';

defineOptions({
  name: 'PorTreeSelect'
});

interface Props {
  url: string;
  clearable?: boolean;
  labelField?: string;
  valueField?: string;
  valueToNumber?: boolean;
  valueJoin?: string | false;
  value?: Array<string | number> | string | number | null;
  multiple?: boolean;
  childrenField?: string;
}

const slots: Slots = useSlots();
const props = withDefaults(defineProps<Props>(), {
  clearable: true,
  labelField: 'name',
  valueField: 'id',
  valueToNumber: false,
  valueJoin: ',',
  options: () => [],
  multiple: false,
  childrenField: 'children',
  value: null
});

const emit = defineEmits<{
  /** 绑定的数据 */
  'update:value': [value: Array<string | number> | string | number | null];
}>();

const value = computed(() => {
  if (!props.multiple) {
    // 处理单选
    if (props.value && typeof props.value !== 'number' && props.valueToNumber) {
      return Number(props.value);
    }
    return props.value;
  }
  // 处理多选
  if (!props.value) return [];
  if (Array.isArray(props.value)) {
    return [...props.value];
  }
  if (typeof props.value === 'string') {
    if (props.valueJoin === false) {
      throw new Error(
        '检测到por-tree-select在多选模式下传入了一个字符串，valueJoin必须为有效的分割字符，否则组件无法解析'
      );
    }

    return props.value.split(props.valueJoin).map(v => {
      if (props.valueToNumber) {
        return Number(v);
      }
      return v;
    });
  }
  return props.value;
});

function onValueChange(v: Array<string | number> | string | number | null) {
  if (!props.multiple) {
    emit('update:value', v);
    return;
  }

  // 多选模式下
  emit(
    'update:value',
    Array.isArray(props.value) || props.valueJoin === false ? v : (v as Array<string | number>).join(props.valueJoin)
  );
}

const dynamicOptions = ref<Array<any>>([]);

function processData(options: any = []) {
  dynamicOptions.value = options.map((item: any) => {
    if (props.valueToNumber) {
      item[props.valueField] = Number(item[props.valueField]);
    }
    return item;
  });
}

const getOptions = (url: string) => {
  return alova.Get<Record<string, any>[]>(url);
};

const url = ref(props.url || '');
// Watch for changes in props.url and update the internal url ref
watch(
  () => props.url,
  (newUrl: string | undefined) => {
    url.value = newUrl || '';
  }
);

const { loading } = useWatcher(
  // 必须设置为返回method实例的函数
  () => getOptions(url.value),
  [url],
  {
    immediate: true
  }
).onSuccess(({ data }) => {
  processData(data);
});
</script>

<template>
  <NTreeSelect
    :loading="loading"
    :clearable="clearable"
    :options="dynamicOptions"
    :label-field="labelField"
    :key-field="valueField"
    :children-field="props.childrenField"
    :multiple="props.multiple"
    :value="value"
    @update:value="onValueChange"
  >
    <template v-for="(_item, key) in slots" :key="key" #[key]="data">
      <slot :name="key" v-bind="data ?? {}" />
    </template>
  </NTreeSelect>
</template>

<style scoped></style>
